package com.beerandjesus.sportofkings.handicapper.dao;

import com.beerandjesus.sportofkings.handicapper.model.*;
import com.beerandjesus.sportofkings.handicapper.dao.aspect.TransactionBoundary;

import java.util.List;
import java.util.ArrayList;
import java.math.BigDecimal;

/**
 * Date: Jun 8, 2012
 * Time: 12:41:51 PM
 */
public class DatabaseImpl implements Database {
    private TurfSireDAO turfSireDAO;
    private JockeyDAO jockeyDAO;
    private boolean initialized = false;

    public DatabaseImpl() {}

    public DatabaseImpl(TurfSireDAO turfSireDAO, JockeyDAO jockeyDAO) {
        this.turfSireDAO = turfSireDAO;
        this.jockeyDAO = jockeyDAO;
    }

    public TurfSire getTurfSireByName(String name) {
        TurfSire byName;
        try {
            byName = getTurfSireDAO().getTurfSireByName(name);
        } catch (DAOException e) {
            throw new DatabaseException("Error getting turf sire " + name, e);
        }
        return byName;
    }

    public JockeyRating getJockeyRatingByName(String jockeyName) {
        if (jockeyName != null) {
            jockeyName = jockeyName.toLowerCase();
        }
        JockeyRating rating;
        try {
            JockeyRatingThreshold thresh = getJockeyDAO().getActiveJockeyRatingThreshold();
            JockeyPerformance perf = getJockeyDAO().getJockeyPerformanceByName(jockeyName);
            rating = calculatJockeyRating(perf, thresh);
        } catch (DAOException e) {
            throw new DatabaseException("Error calculating jockey rating");
        }
        return rating;
    }

    private JockeyRating calculatJockeyRating(JockeyPerformance perf, JockeyRatingThreshold thresh) {
        if (perf == null) {
            return null;
        }
        BigDecimal wr = perf.getWinRatio();
        CramerJockeyRating cjr = CramerJockeyRating.OTHER;
        if (wr.compareTo(thresh.getLeading()) >= 0) {
            cjr = CramerJockeyRating.LEADING;
        } else if (wr.compareTo(thresh.getExcellent()) >= 0) {
            cjr = CramerJockeyRating.EXCELLENT;
        } else if (wr.compareTo(thresh.getGood()) >= 0) {
            cjr = CramerJockeyRating.GOOD;
        }
        JockeyRatingImpl rating = new JockeyRatingImpl();
        rating.setJockeyName(perf.getName());
        rating.setRating(cjr);
        return rating;
    }

    public void save(JockeyRating jockeyRating) {
        try {
            getJockeyDAO().save(jockeyRating);
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey rating " + jockeyRating.getJockeyName() +
                    "-" + jockeyRating.getRating(), e);
        }
    }

    public void save(List<JockeyRating> jockeyRatings) {
        JockeyRating jockeyRating = null;
        try {
            for (JockeyRating r : jockeyRatings) {
                jockeyRating = r;
                getJockeyDAO().save(r);
            }
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey rating " + jockeyRating.getJockeyName() +
                    "-" + jockeyRating.getRating(), e);
        }

    }

    public void savePerformances(List<JockeyPerformance> performances) {
        JockeyPerformance jp = null;
        try {
            for (JockeyPerformance p : performances) {
                jp = p;
                getJockeyDAO().save(p);
            }
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey performance " + jp.getName());
        }

    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public List<JockeyRating> getAllJockeyRatings() {
        ArrayList<JockeyRating> jrs = new ArrayList<JockeyRating>();
        try {
            JockeyRatingThreshold thresh = getJockeyDAO().getActiveJockeyRatingThreshold();
            List<JockeyPerformance> perfs = getJockeyDAO().getAllJockeyPerformances();
            for (JockeyPerformance p : perfs) {
                jrs.add(calculatJockeyRating(p, thresh));
            }
        } catch (DAOException e) {
            throw new DatabaseException("Error getting all jockey ratings", e);
        }
        return jrs;
    }

    public List<JockeyRatingThreshold> getAllJockeyRatingThresholds() {
        try {
            return getJockeyDAO().getAllJockeyRatingThresholds();  //To change body of implemented methods use File | Settings | File Templates.
        } catch (DAOException e) {
            throw new DatabaseException("Error getting all jockey rating thresholds", e);
        }
    }

    public void save(JockeyRatingThreshold threshold) {
        try {
            getJockeyDAO().save(threshold);
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey rating threshold", e);
        }
    }

    public void delete(JockeyRatingThreshold threshold) {
        try {
            getJockeyDAO().delete(threshold);
        } catch (DAOException e) {
            throw new DatabaseException("Error deleting jockey rating threshold", e);
        }
    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public int getJockeyRatingDatabaseRowCount() {
        try {
            return getJockeyDAO().getRowCount();
        } catch (DAOException e) {
            throw new DatabaseException("Error getting jockey rating database row count", e);
        }
    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public int getJockeyPerformanceDatabaseRowCount() {
        try {
            return getJockeyDAO().getJockeyPerformanceRowCount();
        } catch (DAOException e) {
            throw new DatabaseException("Error getting jockey rating database row count", e);
        }
    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public void rebuildJockeyRatingDatabase() {
        try {
            getJockeyDAO().buildDatabase();
        } catch (DAOException e) {
            throw new DatabaseException("Error building jockey rating database", e);
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Jockey database rebuilt");

    }

    @TransactionBoundary(datasources = Datasource.TURF_SIRE)
    public synchronized void init() {
        if (initialized) {
            throw new DatabaseException("Database already initialized");
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": init()");
        int count;
        try {
            getTurfSireDAO().buildDatabase();
            count = getTurfSireDAO().getRowCount();
        } catch (DAOException e) {
            throw new DatabaseException("Error building turf sire database", e);
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Turf sire database initialized, row count: " + count);
        initialized = true;
    }

    public void setTurfSireDAO(TurfSireDAO dao) {
        this.turfSireDAO = dao;
    }

    public TurfSireDAO getTurfSireDAO() {
        return turfSireDAO;
    }

    public JockeyDAO getJockeyDAO() {
        return jockeyDAO;
    }

    public void setJockeyDAO(JockeyDAO jockeyDAO) {
        this.jockeyDAO = jockeyDAO;
    }

}
